package com.dhms.vostok1.repository

import android.util.Log
import com.dhms.uikit.select_bottom_sheet.SelectItem
import com.dhms.vostok1.data.BaseResData
import com.dhms.vostok1.data.TaskGroupsResData
import com.dhms.vostok1.data.spot_patrol.*
import com.dhms.vostok1.model.TaskGroupStatus
import com.dhms.vostok1.network.MoshiObject
import com.dhms.vostok1.network.MoshiObject.moshi
import com.dhms.vostok1.service.net.NetworkApi
import com.dhms.vostok1.utils.toFormatDate
import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.isFrozen
import io.realm.kotlin.ext.query
import io.realm.kotlin.query.find
import io.realm.kotlin.types.ObjectId
import retrofit2.Response
import java.util.*

class SpcTaskRepository {
    val TAG: String = "Offline_SPC_Task"

    private val config = RealmConfiguration.Builder(
        schema = setOf(
            TaskGroupData::class,
            TaskData::class,
            SelectItem::class,
            PatrolItemsData::class,
            FiveSensesData::class,
            MeterReadingData::class,
            SpotDetectorData::class,
            SpotCheckItems::class,
            SpotCheckItem::class,
            SpotCheckFCCItem::class,
            ReportTerminalTask::class,
            AbnormalOssData::class
        )
    )
        .build()

    suspend fun getSpcTask(
        isOnline: Boolean,
        offset: Int,
        limit: Int,
        task_type: List<PlanType>? = null,
        task_status: List<TaskStatus>? = null,
        task_group_status: TaskGroupStatus? = null,
    ): Result<Response<BaseResData<TaskGroupsResData>>?> {
        if (!isOnline) {
            // get offline spc task
            val realm = Realm.open(config)
            val taskGroupDatas = LinkedList<TaskGroupData>()
            realm.query<TaskGroupData>().find().toList().forEach { taskGroupData ->
                when (task_group_status) {
                    TaskGroupStatus.DONE ->
                        if (taskGroupData.taskGroupStatus == TaskStatus.DONE ||
                            taskGroupData.taskGroupStatus == TaskStatus.TIMEOUT_DONE
                        ) {
                            taskGroupDatas.add(taskGroupData)
                        }
                    TaskGroupStatus.UN_DONE ->
                        if (taskGroupData.taskGroupStatus == TaskStatus.UN_START ||
                            taskGroupData.taskGroupStatus == TaskStatus.DOING
                        ) {
                            taskGroupDatas.add(
                                taskGroupData
                            )
                        }
                    TaskGroupStatus.TIMEOUT ->
                        if (taskGroupData.taskGroupStatus == TaskStatus.TIMEOUT) {
                            taskGroupDatas.add(
                                taskGroupData
                            )
                        }
                    TaskGroupStatus.TODO ->
                        if (taskGroupData.taskGroupStatus == TaskStatus.TIMEOUT ||
                            taskGroupData.taskGroupStatus == TaskStatus.DOING
                        ) {
                            taskGroupDatas.add(
                                taskGroupData
                            )
                        }
                    else -> {

                    }
                }

            }
            val baseResData = BaseResData(
                status = 200.toDouble(),
                message = null,
                code = 200,
                data = TaskGroupsResData(
                    data = taskGroupDatas,
                    total = taskGroupDatas.size,
                    limit = taskGroupDatas.size,
                    offset = 0
                )
            )
            val resData = Response.success(baseResData)
            return Result.success<Response<BaseResData<TaskGroupsResData>>?>(resData)
        } else {
            return NetworkApi.getTaskGroups(
                offset,
                limit,
                task_type,
                task_status
            )
        }
    }

    suspend fun getTaskGroupDetail(
        isOnline: Boolean,
        taskGroupId: String
    ): Result<Response<BaseResData<TaskGroupData>>?> {
        if (!isOnline) {
            val realm = Realm.open(config)
            val taskGroupData =
                realm.query<TaskGroupData>("groupID=$0", taskGroupId).find().firstOrNull()
            val baseResData = BaseResData<TaskGroupData>(
                status = 200.toDouble(),
                message = null,
                code = 200,
                data = taskGroupData
            )
            val resData = Response.success(baseResData)
            return Result.success<Response<BaseResData<TaskGroupData>>?>(resData)
        } else {
            val res = NetworkApi.getTaskGroupDetail(taskGroupId)
            return res
        }
    }

    suspend fun getTaskDetail(
        isOnline: Boolean,
        group_id: String,
        task_id: Long
    ): Result<Response<BaseResData<TaskData>>?> {
        if (!isOnline) {
            val realm = Realm.open(config)
            val baseResData = BaseResData<TaskData>(
                status = 200.toDouble(),
                message = null,
                code = 200,
                data = null
            )
            val taskData =
                realm.query<TaskGroupData>("groupID=$0", group_id).first().find {
                    it?.taskList?.find { t ->
                        t.taskId == task_id
                    }
                }
            realm.writeBlocking {
                if (taskData != null) {
                    findLatest(taskData)?.also {
                        val jsonAdapter = moshi.adapter(TaskData::class.java)
                        val json = jsonAdapter.toJson(it)
                        baseResData.data = jsonAdapter.fromJson(json)
                        val isFrozen = it.isFrozen()
                        Log.d(TAG, "getTaskDetail: isFrozen $isFrozen")
                    }
                }
            }

            val resData = Response.success(baseResData)
            return Result.success<Response<BaseResData<TaskData>>?>(resData)
        } else {
            return NetworkApi.getTaskDetail(group_id, task_id)
        }
    }

    suspend fun downloadOfflineSpcTasks(
        task_type: List<PlanType>,
        task_status: List<TaskStatus>,
        lastTime: Long?,
    ): Boolean {
        val res = NetworkApi.getTaskGroups(
            null,
            null,
            task_type,
            task_status,
            lastTime,
            is_remove_complete = true
        )
        if (res.isSuccess) {
            res.getOrNull()?.body()?.data?.let {
                val realm = Realm.open(config)
                it.data?.forEach { taskGroupData: TaskGroupData ->
                    // save taskGroupData in local db
                    try {
                        realm.write {
                            copyToRealm(
                                taskGroupData
                            )
                        }
                    } catch (e: Exception) {
                        if (e is IllegalArgumentException) {
                            Log.e(
                                TAG,
                                "the spc task group (${taskGroupData.groupID}) has been saved in local already"
                            )
                        } else {
                            Log.e(
                                TAG,
                                "failed to save spc task group (${taskGroupData.groupID}) in local. err: ${e.message}"
                            )
                        }
                    }
                }
                realm.close()
            }
        }

        return res.isSuccess
    }


    fun getSpcTaskListData(): List<TaskData> {
        val realm = Realm.open(config)
        return realm.query<TaskData>().find().toList()
    }

    fun getCompletedSpcTaskListData(): List<TaskData> {
        val realm = Realm.open(config)
        return realm.query<TaskData>("completeTime != $0", null).find().toList()
    }

    fun getSpcTaskData(groupID: String, taskId: Long): TaskData? {
        val realm = Realm.open(config)
        return realm.query<TaskData>("groupID=$0 AND taskId=$1", groupID, taskId).find()
            .firstOrNull()
    }

    suspend fun deleteSpcTaskData(groupID: String, taskId: Long) {
        val realm = Realm.open(config)
        realm.write {
            val taskData =
                this.query<TaskData>("groupID=$0 AND taskId=$1", groupID, taskId).find()
                    .firstOrNull()
            taskData?.let {
                delete(it)
            }
        }
        realm.close()
    }

    suspend fun deleteErrorSpcTaskData() {
        val realm = Realm.open(config)
        realm.write {
            val taskList =
                this.query<TaskData>().find().filter { !(it.error.isNullOrBlank()) }
            taskList.forEach {
                delete(it)
            }
        }
        realm.close()
    }

    suspend fun updateSpcTaskDataError(groupID: String, taskId: Long, error: String) {
        val realm = Realm.open(config)
        realm.write {
            val taskData =
                this.query<TaskData>("groupID=$0 AND taskId=$1", groupID, taskId).find()
                    .firstOrNull()
            taskData?.error = error
        }
        realm.close()
    }

    suspend fun updateTask(isOnline: Boolean, task: TaskData): Result<Response<BaseResData<Any>>?> {
        return if (!isOnline) {
            val realm = Realm.open(config)
            try {
                realm.writeBlocking {
                    this.copyToRealm(task, UpdatePolicy.ALL)
                    Log.d(TAG, "update spc task: ${task.groupID} - ${task.taskId}")
                }
                realm.close()
                Result.success(null)
            } catch (ex: Exception) {
                Log.d(TAG, "fail to update spc task. error: " + ex.message)
                realm.close()
                Result.failure(ex)
            }
        } else {
            NetworkApi.updateTask(task)
        }
    }

    // 上传设备信息
    suspend fun uploadTerminalInfo(isOnline: Boolean, terminalInfo: ReportTerminalInfo)
            : Result<Any?> {
        return if (!isOnline) {
            val realm = Realm.open(config)
            try {
                val reportingTask = ReportTerminalTask().apply {
                    this.terminalInfoStr = MoshiObject.moshi.adapter(ReportTerminalInfo::class.java)
                        .toJson(terminalInfo)
                }
                realm.writeBlocking {
                    this.copyToRealm(reportingTask, UpdatePolicy.ALL)
                }
                realm.close()
                Result.success(null)
            } catch (ex: Exception) {
                Log.d(TAG, "fail to update spc task. error: " + ex.message)
                realm.close()
                Result.failure(ex)
            }
        } else {
            NetworkApi.uploadTerminalInfo(terminalInfo)
        }
    }

    suspend fun getReportTerminalTaskData(): List<ReportTerminalTask> {
        val realm = Realm.open(config)
        return realm.query<ReportTerminalTask>().find().toList()
    }

    suspend fun deleteReportTerminalTaskData(id: ObjectId) {
        val realm = Realm.open(config)
        realm.write {
            val data =
                this.query<ReportTerminalTask>("id=$0", id).find()
                    .firstOrNull()
            data?.let {
                delete(it)
            }
        }
        realm.close()
    }

    fun updateAbnormalOssData(
        taskData: TaskData,
        localUrl: String,
        url: String,
        firstFrame:String,
    ): TaskData? {
        val realm = Realm.open(config)
        realm.writeBlocking {
            realm.query<TaskData>(
                "groupID == $0 and taskId == $1",
                taskData.groupID,
                taskData.taskId
            ).first().find()?.also { t ->
                findLatest(t)?.also { data ->
                    data.patrolItems?.fiveSenses?.forEach { fiveSensesData ->
                        fiveSensesData.abnormalOss.forEach { abnormalOssData ->
                            if (abnormalOssData.localUrl == localUrl) {
                                abnormalOssData.url = url
                                abnormalOssData.firstFrame = firstFrame
                            }
                        }
                    }
                    copyToRealm(data, UpdatePolicy.ALL)
                }
            }
        }
        return realm.query<TaskData>(
            "groupID == $0 and taskId == $1",
            taskData.groupID,
            taskData.taskId
        ).first().find()
    }

    suspend fun deleteAllData() {
        try {
            val realm = Realm.open(config)
            realm.write {
                config.schema.forEach {
                    delete(query(it).find())
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "error: $e")
        }
    }

    fun updateTaskGroupStatus(taskGroupId: String) {
        val realm = Realm.open(config)
        realm.writeBlocking {
            val taskGroup = realm.query<TaskGroupData>("groupID == $0", taskGroupId).first().find()
            taskGroup?.let {
                findLatest(it)?.also { g ->
                    val taskStatus = g.taskList.map { t -> t.taskStatus }
                    g.taskGroupStatus = getTaskGroupStatus(taskStatus)
                    if (g.taskGroupStatus == TaskStatus.DONE || g.taskGroupStatus == TaskStatus.TIMEOUT_DONE) {
                        g.taskCompleteTime = Date().toFormatDate(format = "yyyy-MM-dd HH:mm:ss")
                    }
                }
            }
        }
    }

    private fun getTaskGroupStatus(taskStatus: List<TaskStatus?>): TaskStatus? {
        var taskGroupStatus: TaskStatus = TaskStatus.UNKNOWN
        taskStatus.forEach { t ->
            t?.let {
                when (t) {
                    TaskStatus.UN_START -> taskGroupStatus = TaskStatus.UN_START
                    TaskStatus.TIMEOUT -> taskGroupStatus = TaskStatus.TIMEOUT
                    else -> {}
                }
            }
        }

        if (taskGroupStatus == TaskStatus.UNKNOWN) {
            var completeStatus = false
            taskStatus.forEach {
                if (it != TaskStatus.DONE) {
                    completeStatus = true
                }
            }
            if (!completeStatus) {
                taskGroupStatus = TaskStatus.DONE
            }
            taskStatus.forEach {
                if (it == TaskStatus.DOING) {
                    taskGroupStatus = TaskStatus.DOING
                    return@forEach
                }
            }
        }

        if (taskGroupStatus == TaskStatus.UNKNOWN) {
            taskStatus.forEach {
                if (it == TaskStatus.TIMEOUT_DONE) {
                    taskGroupStatus = TaskStatus.TIMEOUT_DONE
                    return@forEach
                }
            }
        }
        return taskGroupStatus
    }

    companion object {
        @Volatile
        private var instance: SpcTaskRepository? = null

        fun getInstance() =
            instance ?: synchronized(this) {
                instance ?: SpcTaskRepository().also { instance = it }
            }
    }
}